﻿using System;
using System.Reflection;
using System.Xml.Linq;
using FluentlyXml.Core.Serialization;

namespace FluentlyXml.Core.BuiltInSerializers
{
	//TODO: This guy and CustomSerializer should probably be merged. 
	internal class ObjectSerializer : ITypeSerializer
	{
		protected PropertySerializerDictionary PropertyFactories { get; private set; }

		public ObjectSerializer()
		{
			PropertyFactories = new PropertySerializerDictionary();
		}

		public virtual bool CanSerialize(Type type)
		{
			return true;
		}

		public virtual XElement Serialize(Type type, object obj, ISerializationContext context)
		{
			var element = new XElement(type.Name);

			SerializeInstanceToElement(context, type, obj, element);

			return element;
		}

		//TODO: Remove type parameter? 
		private void SerializeInstanceToElement(ISerializationContext context, Type type, object obj, XElement element)
		{
			if (obj == null)
			{
				throw new ArgumentNullException("obj");
			}

			if (element == null)
			{
				throw new ArgumentNullException("element");
			}

			foreach (var prop in type.GetPropertiesForSerialization())
			{
				try
				{
					WriteProperty(context, obj, prop, element);
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException("Error serializing property '" + type.FullName + "." + prop.Name + ".", ex);
				}
			}
		}

		private void WriteProperty(ISerializationContext context, object target, PropertyInfo property, XElement parent)
		{
			var elementFactory = PropertyFactories[property];

			var element = elementFactory(context, target, property);

			if (element != null)
			{
				parent.Add(element);
			}
		}
	}
}